/* Copyright (C) 2004 The Trustees of Indiana University. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 * 
 * 2) All redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 * 
 * 3) Any documentation included with all redistributions must include the
 * following acknowledgement:
 * 
 * "This product includes software developed by the Indiana University Extreme!
 * Lab. For further information please visit http://www.extreme.indiana.edu/"
 * 
 * Alternatively, this acknowledgment may appear in the software itself, and
 * wherever such third-party acknowledgments normally appear.
 * 
 * 4) The name "Indiana University" or "Indiana University Extreme! Lab" shall
 * not be used to endorse or promote products derived from this software without
 * prior written permission from Indiana University. For written permission,
 * please contact http://www.extreme.indiana.edu/.
 * 
 * 5) Products derived from this software may not use "Indiana University" name
 * nor may "Indiana University" appear in their name, without prior written
 * permission of the Indiana University.
 * 
 * Indiana University provides no reassurances that the source code provided
 * does not infringe the patent or any other intellectual property rights of any
 * other entity. Indiana University disclaims any liability to any recipient for
 * claims brought by any other entity based on infringement of intellectual
 * property rights or otherwise.
 * 
 * LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO
 * WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 * NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */

package edu.indiana.cs.b534.torrent.impl;

import edu.indiana.cs.b534.torrent.context.TorrentContext;

import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

/**
 * @author : Eran Chinthaka (echintha@cs.indiana.edu)
 * @Date : Mar 25, 2007
 */
public class InfoCollector {

    private String myPeerId;
    private List<Event> events;
    private TorrentContext context;

    public static final String START_DOWNLOAD_EVENT = "StartDownload";
    public static final String FINISH_DOWNLOAD_EVENT = "FinishDownload";
    public static final String START_UPLOAD_EVENT = "StartUpload";
    public static final String FINISH_UPLOAD_EVENT = "FinishUpload";

    // this will contain information about, which pieces we downloaded from which peer
    private Map<String, List<Integer>> downloadInformation = new Hashtable<String, List<Integer>>();
    private Map<String, List<Integer>> uploadInformation = new Hashtable<String, List<Integer>>();
    private long allDownloadStartTime;

    /**
     * @param context
     */
    public InfoCollector(TorrentContext context) {
        this.context = context;
        this.myPeerId = context.getMyPeerId();
        events = new ArrayList<Event>();
    }

    public void notifyStartAllDownloadsEvent() {
        allDownloadStartTime = System.currentTimeMillis();
    }

    /**
     * @param eventName
     * @param connectingPeerId
     * @param pieceIndex
     */
    public void notifyPeerCommunicationEvent(String eventName, String connectingPeerId, int pieceIndex) {
        events.add(new Event(eventName, new Date().getTime(), myPeerId, connectingPeerId, pieceIndex));
    }

    /**
     * @param connectingPeerId
     * @param pieceIndex
     */
    public void notifyStartDownloadEvent(String connectingPeerId, int pieceIndex) {
        this.notifyPeerCommunicationEvent(START_DOWNLOAD_EVENT, connectingPeerId, pieceIndex);
    }

    /**
     * @param connectingPeerId
     * @param pieceIndex
     */
    public void notifyFinishDownload(String connectingPeerId, int pieceIndex) {
        this.notifyPeerCommunicationEvent(FINISH_DOWNLOAD_EVENT, connectingPeerId, pieceIndex);
    }

    /**
     * @param connectingPeerId
     * @param pieceIndex
     */
    public void notifyStartUpload(String connectingPeerId, int pieceIndex) {
        this.notifyPeerCommunicationEvent(START_UPLOAD_EVENT, connectingPeerId, pieceIndex);
    }

    /**
     * @param connectingPeerId
     * @param pieceIndex
     */
    public void notifyFinishUpload(String connectingPeerId, int pieceIndex) {
        this.notifyPeerCommunicationEvent(FINISH_UPLOAD_EVENT, connectingPeerId, pieceIndex);
    }

    public void printSummary() {
        long allDownloadEndTime = new Date().getTime();

        // get the last download completed event
        for (int i = events.size() - 1; i > 0; i--) {
            if (FINISH_DOWNLOAD_EVENT.equals(events.get(i).event)) {
                allDownloadEndTime = events.get(i).time;
                break;
            }
        }

        double downloadRate = context.getTotalDownloadedBytes() / ((allDownloadEndTime - allDownloadStartTime) / 1000);

        StringBuffer outputBuffer = new StringBuffer();
        outputBuffer.append(" ======== Download History ==========\n");
        outputBuffer.append(" My Peer Name = " + context.getMyPeerId() + "\n");

        for (Object o : downloadInformation.keySet()) {
            String peerId = (String) o;
            outputBuffer.append(peerId);
            outputBuffer.append("[");
            List<Integer> piecesList = downloadInformation.get(peerId);
            for (Integer pieceIndex : piecesList) {
                outputBuffer.append(pieceIndex + " ");
            }
            outputBuffer.append("]\n");
        }

        if (downloadRate < 1024 * 1024) {
            outputBuffer.append("Download Rate = " + Math.ceil(downloadRate / (1024)) + " KBps\n");
        } else if (downloadRate < 1024 * 1024 * 1024) {
            outputBuffer.append("Download Rate = " + (downloadRate / (1024 * 1024)) + " MBps\n");
        }
        outputBuffer.append("Time took = " + (allDownloadEndTime - allDownloadStartTime) + "ms\n");

        System.out.println(outputBuffer.toString());
    }

    public void setPieceDownloadedFromPeer(String peerId, int pieceIndex) {

        List<Integer> pieceData;
        if (!downloadInformation.containsKey(peerId)) {
            pieceData = new ArrayList<Integer>();
            downloadInformation.put(peerId, pieceData);
        } else {
            pieceData = downloadInformation.get(peerId);
        }

        pieceData.add(pieceIndex);

    }

    public void setPieceUploadedToPeer(String peerId, int pieceIndex) {

        List<Integer> pieceData;
        if (!uploadInformation.containsKey(peerId)) {
            pieceData = new ArrayList<Integer>();
            uploadInformation.put(peerId, pieceData);
        } else {
            pieceData = uploadInformation.get(peerId);
        }

        pieceData.add(pieceIndex);

    }

    public Map<String, List<Integer>> getDownloadInformation() {
        return this.downloadInformation;
    }


    public Map<String, List<Integer>> getUploadInformation() {
        return uploadInformation;
    }

    class Event {
        private String event;
        private long time;
        private String sourcePeerId;
        private String destPeerId;
        private int pieceIndex;


        /**
         * @param event
         * @param time
         * @param sourcePeerId
         * @param destPeerId
         */
        public Event(String event, long time, String sourcePeerId, String destPeerId, int pieceIndex) {
            this.event = event;
            this.time = time;
            this.sourcePeerId = sourcePeerId;
            this.destPeerId = destPeerId;
            this.pieceIndex = pieceIndex;
        }
    }
}
